/**
* ComponentFactory - Factory which creates XMLComponents from an XML Doc.
*
* Copyright (c) 2000, 2001, 2002
* Marty Phelan, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package com.taursys.xml;
import java.util.*;
import org.w3c.dom.*;
import com.taursys.model.ValueHolder;
import javax.swing.tree.*;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import com.taursys.xml.*;
import com.taursys.dom.DOM_1_20000929_DocumentAdapter;
import com.taursys.debug.Debug;
/**
* Abstract class used to automate the creation of Components based on
* the XML Document and its Elements. It determines the Component type based
* on its element tag and "id" attribute). Concrete subclasses must override
* the <code>initTagTable</code> method and populate the tagTable with
* suggested components for the elements in the XML document.
* <p>
* This class contains two primary methods:
* <ul>
* <li><code>getSuggestedComponents</code> - for use by design tools.</li>
* <li><code>createComponents</code> - to automatically create and bind
* <code>Components</code> at runtime.</li>
* </ul>
*/
public abstract class ComponentFactory {
public static final String ID_DELIMITER = "__";
public static final String TEMPLATE_NODE = "TEMPLATE_NODE";
protected Hashtable tagTable = new Hashtable();
// ***********************************************************************
// * CONSTRUCTORS AND INITIALIZERS
// ***********************************************************************
/**
* Default constructor which initializes tag table by calling initTagTable
*/
public ComponentFactory() {
initTagTable();
}
// ***********************************************************************
// * GENERAL METHODS
// ***********************************************************************
/**
* Returns a Vector of suggested Component class names for given Element.
* This method will choose the appropriate Components based on the type of
* Element given. The default Component type will be the first in the list.
* <p>
* If the Element has an ID, then the DocumentElement Component will be added
* to the end of the suggestion list.
* <p>
* If the id contains the TEMPLATE_NODE keyword, then a Template will be added
* to the top of the suggestion list.
* <p>
* If there are no suggested types of Component for the given Element, then
* an empty Vector will be returned.
* <p>
* Subclasses should override this method if more than the Element tag name
* is needed to determine the suggested components.
* @param element to return default Component type for
* @return a Vector containing any suggested Component class names
*/
public abstract Vector getSuggestedComponents(Element element);
/**
* <p>Creates components based on document, set their properties (including
* valueHolder) and adds them to the container. As it moves through the
* document, it first checks to see if the component is already in the
* container (by matching id's). If it is, the existing component is moved
* to the proper place in the heirarchy. Otherwise it will create a component.
* </p>
* <p>This method builds a component heirarchy which matches the document order
* and heirarchy. If any newly created component is itself a Container type,
* then all children of that component are added to it rather than its parent
* container.
* </p>
* <p>Only bound components which are bound to one of the given value holders
* are created.</p>
* @param container the Container to add components to
* @param holders an array of valueholders.
*/
public void createComponents(Container container, ValueHolder[] holders) {
// invoke private method to recursively create components
createComponents(
container.getDocumentAdapter().getDocument(),
container,
holders,
container);
}
// ***********************************************************************
// * PROTECTED AND PRIVATE METHODS
// ***********************************************************************
/**
* Initialize values in the tag table
*/
protected abstract void initTagTable();
/**
* Returns a Vector of suggested Component class names for given Element.
* This method will choose the appropriate Components based on the type of
* tag given. The default Component type will be the first in the list.
* <p>
* If an ID is given, then the DocumentElement Component will be added to the
* end of the suggestion list.
* <p>
* If the id contains the TEMPLATE_NODE keyword, then a Template will be added
* to the top of the suggestion list.
* <p>
* If an ID is given and the id does not contain the TEMPLATE_NODE keyword,
* but the given element has child nodes, then a Template will be added before
* the DocumentElement suggestion.
* <p>
* If there are no suggested types of Component for the given tag, then
* an empty Vector will be returned.
* @param tagName to return default Component type for
* @param id of the tag or null
* @param element the Element to get suggestions for
* @return a Vector containing any suggested Component class names
*/
protected Vector getSuggestedComponents(
String tagName, String id, Element element) {
Vector suggestions = (Vector)tagTable.get(tagName);
if (suggestions == null)
suggestions = new Vector();
if (id != null && id.length() > 0) {
// add Template at beginning or end of suggestions
if (id.indexOf(TEMPLATE_NODE) > -1) {
suggestions.add(0,Template.class.getName());
} else {
if (DOM_1_20000929_DocumentAdapter.hasChildElements(element))
suggestions.add(Template.class.getName());
}
suggestions.add(DocumentElement.class.getName());
}
return suggestions;
}
/**
* Create a component for given element and set its properties.
* @param id the id of the Element to create the Component for.
* @param element the Element to create the Component for.
* @param holders the array of ValueHolders for binding
*/
protected abstract Component createComponentForElement(
String id, Element element, ValueHolder[] holders);
/**
* <p>Creates components based on document, set their properties (including
* valueHolder) and adds them to the container. As it moves through the
* document, it first checks to see if the component is already in the
* container (by matching id's). If it is, the existing component is moved
* to the proper place in the heirarchy. Otherwise it will create a component.
* </p>
* <p>This method builds a component heirarchy which matches the document order
* and heirarchy. If any newly created component is itself a Container type,
* then all children of that component are added to it rather than its parent
* container.
* </p>
* <p>Only bound components which are bound to one of the given value holders
* are created.</p>
* @param parentNode the parent node of the children to process and recurse
* @param parentContainer the current Container to add components to
* @param holders an array of valueholders.
* @param rootContainer the top Container which may contain existing
* Components
*/
private void createComponents(Node parentNode, Container parentContainer,
ValueHolder[] holders, Container rootContainer) {
Node childNode = parentNode.getFirstChild();
Component childComponent = null;
while (childNode != null) {
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
String id = ((Element)childNode).getAttribute("id");
if (id != null && id.length() > 0) {
// See if already exists
childComponent = (Component)rootContainer.get(id);
if (childComponent != null) {
// remove from existing position
rootContainer.remove(childComponent);
traceCreateComponents("Element with existing component - moving component. id=" + id + " element=" + childNode);
} else {
// create component
childComponent = createComponentForElement(
id, (Element)childNode, holders);
traceCreateComponents("Creating new component for element. id=" + id
+ " element=" + childNode + " componentType=" + childComponent);
}
// Add component to parent (if not null)
if (childComponent != null)
parentContainer.add(childComponent);
} else {
childComponent = null;
traceCreateComponents("Skipped element with no id. Element=" + childNode);
}
if (childComponent instanceof Container)
createComponents(
childNode, (Container)childComponent, holders, rootContainer);
else
createComponents(childNode, parentContainer, holders, rootContainer);
}
childNode = childNode.getNextSibling();
}
}
private void traceCreateComponents(String msg) {
Debug.debug("ComponentFactory.createComponents: " + msg);
}
}